package org.project.d.network.start;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NetUtil;
import lombok.extern.slf4j.Slf4j;
import org.project.d.network.Connection;
import org.project.d.network.NettyConnection;
import org.project.d.network.TcpConnectionManager;
import org.project.d.network.coder.TcpCombinedChannelDuplexHandler;
import org.project.d.network.handler.ConnectionEvent;
import org.project.d.network.handler.ConnectionHandler;
import org.project.d.network.handler.HeartEvent;
import org.project.d.network.handler.MsgDispatcherHandler;

import java.net.InetSocketAddress;

/**
 * @author lin
 * @version V1.0
 * @description
 * @date 2020/9/29 11:48 上午
 */
@Slf4j
public class TcpClient extends NettyTCPClient<Connection> {

    protected ChannelOptionInit channelOptionInit;
    protected ChannelHandlerInit channelHandlerInit = new ChannelHandlerInit() {
        private final ChannelHandler tcpCombinedChannelDuplexHandler=new TcpCombinedChannelDuplexHandler();
        private final ChannelHandler connectionHand=new ConnectionHandler(ConnectionEvent.builder().heartEvent(new HeartEvent() {
            @Override
            public void readerIdle(IdleStateEvent idleStateEvent, Connection connection) {

            }

            @Override
            public void writerIdle(IdleStateEvent idleStateEvent, Connection connection) {

            }

            @Override
            public void allIdle(IdleStateEvent idleStateEvent, Connection connection) {
                connection.close();
            }
        }).build());
        private final ChannelHandler msgDispatcherHandler=new MsgDispatcherHandler();

        @Override
        public void channelInit(ChannelPipeline pipeline) {
            pipeline.addLast(new IdleStateHandler(0, 0, 60));
            pipeline.addLast(new TcpCombinedChannelDuplexHandler());
            pipeline.addLast(connectionHand);
            pipeline.addLast(msgDispatcherHandler);

        }
    };

    public TcpClient() {
    }

    public TcpClient(ChannelHandlerInit channelHandlerInit) {
        this.channelHandlerInit = channelHandlerInit;
    }

    public TcpClient(ChannelOptionInit channelOptionInit) {
        this.channelOptionInit = channelOptionInit;
    }

    public TcpClient(ChannelOptionInit channelOptionInit, ChannelHandlerInit channelHandlerInit) {
        this.channelOptionInit = channelOptionInit;
        this.channelHandlerInit = channelHandlerInit;
    }

    public Connection connectAndGetConnection(String host, int port, Listener listener, boolean sync) {
//      XXX   TcpConnectionManager 可以重构为Connection pool
        Connection nettyConnection = TcpConnectionManager.computeIfAbsent(NetUtil.toSocketAddressString(host, port), (s)->new NettyConnection());
        if (nettyConnection.isConnected()){
            return nettyConnection;
        }
        Listener connectionListener = new Listener() {
            @Override
            public void onSuccess(Object... args) {
                if (listener!=null)listener.onSuccess(nettyConnection);
            }

            @Override
            public void onFailure(Throwable cause) {
                if (listener!=null)listener.onFailure(cause);
            }
        };
        ChannelFuture connect = super.connect0(host, port, connectionListener);
        if (sync) {
            try {
                connect.sync();
            } catch (InterruptedException e) {
                log.error("sync err", e);
            }
        }
        nettyConnection.init(connect.channel(), new InetSocketAddress(host, port), NettyConnection.CHANNEL_TYPE_TCP, null);
        return nettyConnection;
    }
    protected void initOptions(Bootstrap b) {
        super.initOptions(b);
        if (channelOptionInit!=null){
            channelOptionInit.channelInit(b);
        }
    }
    @Override
    protected void initPipeline(ChannelPipeline pipeline) {
        channelHandlerInit.channelInit(pipeline);
    }

    @Override
    public Connection connect(String host, int port) {
        return connectAndGetConnection(host, port, null, false);
    }

    @Override
    public Connection connect(String host, int port, boolean sync) {
        return connectAndGetConnection(host, port, null, sync);
    }

    @Override
    public Connection connect(String address, boolean sync) {
        return connect(address,null,sync);
    }

    @Override
    public Connection connect(String address) {
        return connect(address,null,false);
    }

    @Override
    public Connection connect(String address, Listener listener, boolean sync) {
        String[] split = address.split(":");
        if (split.length<1){
            throw new RuntimeException("address:{}"+address+" err");
        }
        return connect(split[0],Integer.parseInt(split[1]),listener,sync);
    }

    @Override
    public Connection connect(String host, int port, Listener listener) {
        return connectAndGetConnection(host, port, listener, false);
    }

    @Override
    public Connection connect(String host, int port, Listener listener, boolean sync) {
        return connectAndGetConnection(host, port, listener, sync);
    }
}
